package com.crack.smartgit.allkill.transformer;

import com.crack.smartgit.LogHelper;
import com.crack.smartgit.VersionMap;
import jdk.internal.org.objectweb.asm.Type;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.List;

/**
 * 目标：通杀
 *
 * @author Administrator
 * @descript
 * @date 2019-08-25
 */
public class SmartGitTransformer implements ClassFileTransformer {

    public SmartGitTransformer() {

    }

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        if (className != null && !"".equals(className.trim())) {
            try {
                ClassReader classReader = new ClassReader(classfileBuffer);
                ClassNode classNode = new ClassNode();
                classReader.accept(classNode, 0);
                List<MethodNode> methodNodes = classNode.methods;
                if (classNode.name.contains(VersionMap.ALL_KILL.get(VersionMap.ENTRY))) {
                    //LogHelper.Debug("classNode.name="+classNode.name+",methodNodes.size="+methodNodes.size());
                    for (final MethodNode methodNode : methodNodes) {
                        Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc);
                        Type returnType = Type.getReturnType(methodNode.desc);

                        String argumentTypeString = "";
                        for (final Type argumentType : argumentTypes) {
                            argumentTypeString += (argumentType.getClassName() + ",");
                        }
                        if (Type.VOID_TYPE != returnType) {
                            continue;
                        }
                        //自22.1版本后,patch函数参数从2个变更为3个
                        if (argumentTypes.length < 2 || argumentTypes.length > 3) {
                            continue;
                        }
                        if (!methodNode.desc.contains(VersionMap.ALL_KILL.get(VersionMap.PARMTYPELIST))) {
                            continue;
                        }
                        if (!argumentTypes[0].getClassName().equals("java.lang.String") && argumentTypes[1].getClassName().equals("java.security.MessageDigest")) {
                            LogHelper.Debug("找到patch函数01=" + classNode.name + "." + methodNode.name + "(" + argumentTypeString + ")" + ",desc=" + methodNode.desc);
                            boolean haveMath = false;
                            InsnList insnList = methodNode.instructions;
                            for (int i = 0; i < insnList.size(); i++) {
                                int opcode = insnList.get(i).getOpcode();
                                if (opcode == Opcodes.INVOKESTATIC) {
                                    MethodInsnNode methodInsnNode = (MethodInsnNode) insnList.get(i);
                                    if (methodInsnNode.owner.contains(VersionMap.ALL_KILL.get(VersionMap.METHOD)) && methodInsnNode.name.equals("min")) {
                                        haveMath = true;
                                        insnList.set(insnList.get(0), new InsnNode(Opcodes.RETURN));
                                        //insnList.insert(insnList.get(0), new InsnNode(Opcodes.RETURN));
                                        methodNode.exceptions.clear();
                                        methodNode.visitEnd();
                                        break;
                                    }
                                }
                            }
                            if (haveMath) {
                                LogHelper.Debug("找到patch函数02=" + classNode.name + "." + methodNode.name + "(" + argumentTypeString + ")" + ",signature=" + methodNode.signature);

                                ClassWriter cw = new ClassWriter(0);
                                classNode.accept(cw);
                                byte[] classCode = cw.toByteArray();
                                try {
                                    FileOutputStream fos = new FileOutputStream(new File("D:\\dumped_" + classNode.name.replaceAll("/", "_") + ".class"));
                                    fos.write(classCode, 0, classCode.length);
                                    fos.close();
                                } catch (Exception e) {
                                    LogHelper.Error("dump class失败,错误=" + e.getMessage());
                                }
                                return classCode;
                            }
                        }
                    }
                }
            } catch (Exception e) {

            }
        }
        return classfileBuffer;
    }
}
